package fudan.se.project.service.advanced.conference.action;

import fudan.se.project.controller.request.OnlyIdPropertyRequest;
import fudan.se.project.controller.request.PostReplyRequest;
import fudan.se.project.controller.request.ReviewInfoSubmitRequest;
import fudan.se.project.controller.response.BaseResponse;
import fudan.se.project.controller.response.PaperResourceResponse;
import fudan.se.project.controller.response.ReviewInfoResponse;
import fudan.se.project.domain.User;
import fudan.se.project.domain.conference.Conference;
import fudan.se.project.domain.conference.Conference.ConferenceStage;
import fudan.se.project.domain.conference.post.Post;
import fudan.se.project.domain.conference.post.Rebuttal;
import fudan.se.project.domain.conference.post.Reply;
import fudan.se.project.domain.conference.production.Paper;
import fudan.se.project.domain.conference.production.ReviewCopy;
import fudan.se.project.domain.conference.production.ReviewInfo;
import fudan.se.project.domain.conference.role.PcMember;
import fudan.se.project.repository.projection.PaperProjection;
import fudan.se.project.repository.projection.ReviewInfoProjection;
import fudan.se.project.repository.projection.customized.ReviewFromCopyProjection;
import fudan.se.project.service.infrastructure.entity.conference.ConferenceService;
import fudan.se.project.service.infrastructure.entity.conference.post.PostService;
import fudan.se.project.service.infrastructure.entity.conference.post.RebuttalService;
import fudan.se.project.service.infrastructure.entity.conference.post.ReplyService;
import fudan.se.project.service.infrastructure.entity.conference.production.PaperService;
import fudan.se.project.service.infrastructure.entity.conference.production.ReviewCopyService;
import fudan.se.project.service.infrastructure.entity.conference.production.ReviewInfoService;
import fudan.se.project.service.infrastructure.entity.conference.role.PcMemberService;
import fudan.se.project.service.infrastructure.func.conference.action.pcmember.ReviewInfoSubmitService;
import fudan.se.project.util.http.ResponseWrapperUtil;
import fudan.se.project.util.user.UserDetailsUtil;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
@RequiredArgsConstructor
public class PcMemberActionService {

    @NonNull
    private final UserDetailsUtil userDetailsUtil;
    @NonNull
    private final ResponseWrapperUtil responseWrapperUtil;

    @NonNull
    private final PcMemberService pcMemberService;
    @NonNull
    private final PostService postService;
    @NonNull
    private final ReplyService replyService;
    @NonNull
    private final RebuttalService rebuttalService;

    @NonNull
    private final ConferenceService conferenceService;
    @NonNull
    private final PaperService paperService;

    @NonNull
    private final ReviewInfoService reviewInfoService;
    @NonNull
    private final ReviewCopyService reviewCopyService;
    @NonNull
    private final ReviewInfoSubmitService reviewInfoSubmitService;

    private static final String ARGUMENTS_ERROR = "arguments error";
    private static final String SUCCESS = "success";



    @PreAuthorize("hasAuthority('USER') and hasPermission(null ,'conference:pcmember:'+#id)")
    public ResponseEntity<?> getTasks(long id, boolean completed) {

        Conference conference = conferenceService.findById(id);

        if (conference.getStage().ordinal() < ConferenceStage.REVIEW_START.ordinal()) {
            return responseWrapperUtil.buildBaseResponseBody(ARGUMENTS_ERROR, HttpStatus.NOT_ACCEPTABLE);
        } else {
            List<Paper> paperList = paperService.findPapersForReviewers(userDetailsUtil.currentUser(), conference, completed);
            PaperResourceResponse paperResourceResponse = new PaperResourceResponse(PaperProjection.project(paperList));
            return responseWrapperUtil.buildResponseBody(paperResourceResponse, HttpStatus.OK);

        }
    }

    @PreAuthorize("hasAuthority('USER') and hasPermission(null ,'paper:pcmember:'+#id)")
    public ResponseEntity<?> getTask(long id) {
        User reviewer = userDetailsUtil.currentUser();
        ReviewInfo reviewInfo = reviewInfoService.findByPaperId(reviewer, id);
        ReviewCopy reviewCopy = reviewCopyService.findByReviewInfo(reviewInfo);
        final Rebuttal rebuttal = rebuttalService.findByPaperId(id);

        ReviewInfoProjection projection = (reviewCopy == null) ?
                ReviewInfoProjection.project(reviewInfo, rebuttal) :
                ReviewFromCopyProjection.project(reviewCopy, rebuttal);
        return responseWrapperUtil.buildResponseBody(
                new ReviewInfoResponse(projection),
                HttpStatus.OK);
    }

    @PreAuthorize("hasAuthority('USER') and hasPermission(null ,'ReviewInfo:pcmember:'+#request.id)")
    public ResponseEntity<?> submitReviewInfo(ReviewInfoSubmitRequest request) {

        ReviewInfo reviewInfo = reviewInfoService.findById(request.getId());

        if (!reviewInfo.getPaper().getConference().getStage().equals(ConferenceStage.REVIEW_START)) {
            return responseWrapperUtil.buildBaseResponseBody(ARGUMENTS_ERROR, HttpStatus.NOT_ACCEPTABLE);
        }

        if (!reviewInfoSubmitService.process(reviewInfo, request)) {
            return responseWrapperUtil.buildBaseResponseBody("conflict", HttpStatus.CONFLICT);
        } else {
            return responseWrapperUtil.buildBaseResponseBody(SUCCESS, HttpStatus.OK);
        }

    }

    @PreAuthorize("hasAuthority('USER') and hasPermission(null ,'post:pcmember:'+#request.id)")
    public ResponseEntity<?> replyPost(PostReplyRequest request) {
        final Post post = postService.findById(request.getId());
        final PcMember pcMember = pcMemberService.findByUserAndPost(userDetailsUtil.currentUser(), post);

        final Reply reply = new Reply(post, pcMember, request.getComment());
        replyService.save(reply);

        return responseWrapperUtil.buildBaseResponseBody(SUCCESS, HttpStatus.OK);
    }

    @PreAuthorize("hasAuthority('USER') and hasPermission(null ,'ReviewInfo:pcmember:'+#request.id)")
    public ResponseEntity<?> confirmReviewInfo(OnlyIdPropertyRequest request) {
        ReviewInfo reviewInfo = reviewInfoService.findById(request.getId());

        if (!postService.isPostReplied(reviewInfo)) {
            return responseWrapperUtil.buildBaseResponseBody(
                    "should discuss before confirm.",
                    HttpStatus.CONFLICT);
        }

        BaseResponse baseResponse = new BaseResponse();

        if (reviewInfoService.isReviewInfoRevisable(reviewInfo)) {
            reviewInfoService.updateVersion(reviewInfo);
            baseResponse.setMessage(SUCCESS);

            return responseWrapperUtil.buildResponseBody(baseResponse, HttpStatus.OK);
        } else {
            baseResponse.setMessage("wrong conference stage");
            return responseWrapperUtil.buildResponseBody(baseResponse, HttpStatus.NOT_ACCEPTABLE);
        }

    }

    @PreAuthorize("hasAuthority('USER') and hasPermission(null ,'ReviewInfo:pcmember:'+#request.id)")
    public ResponseEntity<?> reviseReviewInfo(ReviewInfoSubmitRequest request) {

        BaseResponse baseResponse = new BaseResponse();

        final int stateBound = ReviewInfo.ReviewInfoState.values().length;
        final int confidenceBound = ReviewInfo.ReviewConfidence.values().length;

        if ((request.getReviewInfoState() < 0 || request.getReviewInfoState() >= stateBound) ||
                (request.getReviewConfidence() < 0 || request.getReviewConfidence() >= confidenceBound)
        ) {
            baseResponse.setMessage(ARGUMENTS_ERROR);
            return responseWrapperUtil.buildResponseBody(baseResponse, HttpStatus.NOT_ACCEPTABLE);
        }

        ReviewInfo reviewInfo = reviewInfoService.findById(request.getId());
        if (!postService.isPostReplied(reviewInfo)) {
            return responseWrapperUtil.buildBaseResponseBody(
                    "should discuss before revise.",
                    HttpStatus.CONFLICT);
        }


        if (!reviewInfoService.isReviewInfoRevisable(reviewInfo)) {
            baseResponse.setMessage("wrong conference stage");
            return responseWrapperUtil.buildResponseBody(baseResponse, HttpStatus.NOT_ACCEPTABLE);

        }

        final ReviewInfo.ReviewInfoState state =
                ReviewInfo.ReviewInfoState.values()[request.getReviewInfoState()];
        final ReviewInfo.ReviewConfidence confidence =
                ReviewInfo.ReviewConfidence.values()[request.getReviewConfidence()];

        if (reviewInfo.getVersion() == 0) {
            reviewInfo.setComment(request.getComment());
            reviewInfo.setReviewInfoState(state);
            reviewInfo.setReviewConfidence(confidence);
            reviewInfo = reviewInfoService.save(reviewInfo);
        } else {
            ReviewCopy reviewCopy = new ReviewCopy(reviewInfo);
            reviewCopy.setComment(request.getComment());
            reviewCopy.setReviewConfidence(confidence);
            reviewCopy.setReviewInfoState(state);
            reviewInfo = reviewCopyService.save(reviewCopy).getReviewInfo();
        }

        reviewInfoService.updateVersion(reviewInfo);

        baseResponse.setMessage(SUCCESS);
        return responseWrapperUtil.buildResponseBody(baseResponse, HttpStatus.OK);

    }
}
